/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */

package org.openmrs.module.datalogging.advice;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Patient;
import org.openmrs.api.context.Context;
import org.openmrs.module.datalogging.configuration.Event;
import org.openmrs.module.datalogging.configuration.Variable;
import org.openmrs.module.datalogging.enums.EventType;
import org.openmrs.module.datalogging.enums.VariableType;
import org.openmrs.module.datalogging.handler.event.EventHandler;
import org.openmrs.module.datalogging.handler.event.EventHandlerFactory;
import org.openmrs.module.datalogging.handler.variable.LogicVariableHandler;
import org.openmrs.module.datalogging.handler.variable.VariableHandler;
import org.openmrs.module.datalogging.handler.variable.VariableHandlerFactory;
import org.openmrs.module.datalogging.model.LogEntry;
import org.openmrs.module.datalogging.model.LogEvent;
import org.openmrs.module.datalogging.service.DataLoggingService;
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 *
 */
public class RuleExecutedAdvice implements AfterReturningAdvice {

	private static final Log log = LogFactory.getLog(RuleExecutedAdvice.class);

	/**
	 * @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object,
	 *      java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
	 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		if (method.getName().startsWith("eval")) {

			if (log.isDebugEnabled())
				log.debug("Processing event rule processing...");

			try {
				Integer patientId = NumberUtils.toInt(String.valueOf(args[0]), -1);
				Patient patient = Context.getPatientService().getPatient(patientId);
				if (patient != null) {
					String rule = String.valueOf(args[1]);
					DataLoggingService service = Context.getService(DataLoggingService.class);
					List<Event> events = service.getEventsByEvent(EventType.EVENT_LOGIC_EXECUTED);
					for (Event event : events) {
						EventHandler eventHandler = EventHandlerFactory.getHandler(event.getEventType());
						if (eventHandler.allow(event, rule)) {
							LogEvent logEvent = new LogEvent();
							logEvent.setPerson(patient);
							logEvent.setEvent(event);
							for (Variable variable : event.getVariables()) {
								LogEntry entry = new LogEntry();
								entry.setLogEvent(logEvent);
								entry.setVariable(variable);
								entry.setPerson(patient);
								Object passedObject = returnValue;
								if (variable.getVariableType() == VariableType.LOGIC_COMPONENT)
									if (variable.getValueExpression().equals(LogicVariableHandler.RULE_NAME))
										passedObject = args[1];
								VariableHandler handler = VariableHandlerFactory.getHandler(variable.getVariableType());
								Object object = handler.handle(patient, variable, passedObject);
								if (object != null) {
									if (ClassUtils.isAssignable(object.getClass(), Date.class))
										entry.setValueDatetime((Date) object);
									else if (ClassUtils.isAssignable(object.getClass(), Double.class))
										entry.setValueNumeric((Double) object);
									else
										entry.setValueText(String.valueOf(object));
								}
								logEvent.addEntry(entry);
							}
							service.saveLogEvent(logEvent);
						}
					}
				}
			} catch (Exception e) {
				log.error("Bailing out from executing advice due to exception.", e);
			}
		}
	}
}
